import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../resources/kq_pad_theme_colors.dart';
import 'base/kq_pad_chart_controller.dart';
import 'base/kq_pad_chart_line_bae_mode.dart';
import 'base/kq_pad_chart_method.dart';
import 'base/kq_pad_chart_model.dart';
import 'kq_pda_chart_line_model.dart';
import 'package:path_drawing/path_drawing.dart';
import 'package:kq_flutter_core_widget/utils/ex/kq_ex.dart';

class KqPdaChartLine extends StatefulWidget {
  ///内部参数设置
  final KqPdaChartLineModel chartModel;

  ///控住多图刷新的key，同一页面有多个图是必填
  final String? keyTag;

  ///背景色
  final Color? backGroundColor;

  ///总高度
  final double? height;

  ///总宽度
  final double? width;

  final List<List<KqPdaChartLineValue>>? valueList;

  final List<List<KqPdaChartLineValue>>? rightValueList;

  const KqPdaChartLine(
      {Key? key,
      required this.chartModel,
      this.keyTag,
      this.height,
      this.width,
      this.valueList,
      this.rightValueList,
      this.backGroundColor})
      : super(key: key);

  @override
  State<KqPdaChartLine> createState() => _KqPadChartLineState();
}

class _KqPadChartLineState extends State<KqPdaChartLine>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;
  late KqPdaChartLineModel lineModel;
  late KqPadChartController self;

  @override
  void initState() {
    super.initState();
    self = Get.put(KqPadChartController(), tag: widget.keyTag);
    lineModel = widget.chartModel;
    lineModel.setValueList(widget.valueList ?? widget.chartModel.chartArray,
        widget.chartModel.chartModel,
        rightList: widget.rightValueList ?? widget.chartModel.chartRightArray);
    controller = AnimationController(
      duration: lineModel.chartModel.duration,
      vsync: this,
    );
    animation = Tween(begin: 0.0, end: 1.0).animate(controller)
      ..addListener(() {
        lineModel.chartModel.animationTime = animation.value;
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          lineModel.chartModel.isAnimationEnd = true;
        }
      });
    //启动动画(正向执行)
  }

  @override
  void didUpdateWidget(covariant KqPdaChartLine oldWidget) {
    super.didUpdateWidget(oldWidget);
    lineModel = widget.chartModel;
    lineModel.setValueList(widget.valueList ?? widget.chartModel.chartArray,
        widget.chartModel.chartModel,
        rightList: widget.rightValueList ?? widget.chartModel.chartRightArray);
  }

  @override
  dispose() {
    //路由销毁时需要释放动画资源
    controller.dispose();
    super.dispose();
  }

  KqPdaLineChartPainter getLineChart() {
    KqPadChartModel model = lineModel.chartModel;
    KqPdaLineChartPainter painter = KqPdaLineChartPainter(
        yStyle: TextStyle(fontSize: model.yFont, color: model.yColor),
        xStyle: TextStyle(fontSize: model.xFont, color: model.xColor),
        chartModel: model,
        xDividers: lineModel.xDividerList,
        yDividers: lineModel.yDividerList,
        lines: lineModel.chartArray,
        rightLines: lineModel.chartRightArray,
        baseModel: lineModel.baseModel);
    return painter;
  }

  @override
  Widget build(BuildContext context) {
    lineModel.chartModel.isAnimationEnd = false;
    lineModel.chartModel.animationTime = 0;
    controller.forward(from: 0);
    KqPadChartModel item = lineModel.chartModel;
    return Container(
      width: widget.width,
      height: widget.height,
      color: widget.backGroundColor,
      child: RepaintBoundary(
          child: Stack(
        children: [
          AnimatedBuilder(
            animation: animation,
            builder: (context, child) {
              return GetBuilder(
                  init: self,
                  tag: widget.keyTag,
                  builder: (controller) => CustomPaint(
                        size: Size(item.sizeWidth, item.sizeHeight),
                        foregroundPainter: widget.backGroundColor != null
                            ? getLineChart()
                            : null,
                        painter: widget.backGroundColor == null
                            ? getLineChart()
                            : null,
                        child: widget.backGroundColor != null
                            ? Container(
                                alignment: Alignment.center,
                                width: item.sizeWidth,
                                height: item.sizeWidth,
                              )
                            : null,
                      ));
            },
          ),
          Padding(
            padding: EdgeInsets.only(
                left: item.xLeft!,
                top: item.yTop!,
                right: item.xRight!,
                bottom: item.yBottom!),
            child: SizedBox(
              width: lineModel.baseModel.xWidth,
              height: lineModel.baseModel.yHeight,
              child: GestureDetector(
                onPanDown: (details) {
                  if (!item.isShowValue) {
                    if (item.isShowClickValue) {
                      for (int i = 0; i < lineModel.chartArray.length; i++) {
                        List<KqPdaChartLineValue> list =
                            lineModel.chartArray[i];
                        for (int j = 0; j < list.length; j++) {
                          KqPdaChartLineValue info = list[j];
                          double x = info.xPoint - item.xLeft!;
                          double y = info.yPoint - item.yTop!;
                          if ((x - 10 < details.localPosition.dx &&
                                  x + 10 > details.localPosition.dx) &&
                              (y - 10 < details.localPosition.dy &&
                                  y + 10 > details.localPosition.dy)) {
                            info.isShow = true;
                            item.onLineChartPointTap?.call(
                              true,
                              i,
                              j,
                              info,
                            );
                          } else {
                            info.isShow = false;
                          }
                        }
                      }
                      if (lineModel.chartRightArray != null) {
                        for (int i = 0;
                            i < lineModel.chartRightArray!.length;
                            i++) {
                          List<KqPdaChartLineValue> list =
                              lineModel.chartRightArray![i];
                          for (int j = 0; j < list.length; j++) {
                            KqPdaChartLineValue info = list[j];
                            double x = info.xPoint - item.xLeft!;
                            double y = info.yPoint - item.yTop!;
                            if ((x - 10 < details.localPosition.dx &&
                                    x + 10 > details.localPosition.dx) &&
                                (y - 10 < details.localPosition.dy &&
                                    y + 10 > details.localPosition.dy)) {
                              info.isShow = true;
                              item.onLineChartPointTap?.call(
                                false,
                                i,
                                j,
                                info,
                              );
                            } else {
                              info.isShow = false;
                            }
                          }
                        }
                      }
                    }
                    self.update();
                  }
                },
                onPanUpdate: (details) {
                  if (!item.isShowValue) {
                    if (item.isShowSlideValue) {
                      for (List<KqPdaChartLineValue> list
                          in lineModel.chartArray) {
                        for (KqPdaChartLineValue info in list) {
                          double x = info.xPoint - item.xLeft!;
                          if (x - 10 < details.localPosition.dx &&
                              x + 10 > details.localPosition.dx) {
                            info.isShow = true;
                          } else {
                            info.isShow = false;
                          }
                        }
                      }
                      if (lineModel.chartRightArray != null) {
                        for (List<KqPdaChartLineValue> list
                            in lineModel.chartRightArray!) {
                          for (KqPdaChartLineValue info in list) {
                            double x = info.xPoint - item.xLeft!;
                            if (x - 10 < details.localPosition.dx &&
                                x + 10 > details.localPosition.dx) {
                              info.isShow = true;
                            } else {
                              info.isShow = false;
                            }
                          }
                        }
                      }
                    }
                    self.update();
                  }
                },
                onPanEnd: (DragEndDetails e) {},
              ),
            ),
          )
        ],
      )),
    );
  }
}

//
class KqPdaLineChartPainter extends CustomPainter {
  ///基本数据
  final KqPadChartModel chartModel;

  final KqPadChartLineBaseModel baseModel;

  ///Y轴文案样式
  final TextStyle yStyle;

  ///X轴文案样式
  final TextStyle xStyle;

  ///
  final List<KqPdaDividerValue>? xDividers;

  final List<KqPdaDividerValue>? yDividers;

  ///具体数据
  final List<List<KqPdaChartLineValue>> lines;

  final List<List<KqPdaChartLineValue>>? rightLines;

  List<LineCanvasModel> lineModels = [];

  List<LineCanvasModel> lineRightModels = [];

  KqPdaLineChartPainter(
      {Key? key,
      this.xDividers,
      this.yDividers,
      required this.baseModel,
      required this.chartModel,
      required this.yStyle,
      required this.xStyle,
      required this.lines,
      this.rightLines})
      : super();

  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..strokeWidth = 1
      ..style = PaintingStyle.stroke;
    KqPadChartMethod.drawXYAxisPath(
        canvas: canvas,
        paint: paint,
        angle: chartModel.angle,
        axisColor: chartModel.axisColor!,
        xList: baseModel.xList,
        yList: baseModel.yList,
        yRightValue: baseModel.yRightValue,
        xRight: chartModel.xRight!,
        isDivide: chartModel.isDivide,
        dividerColor: chartModel.dividerColor!,
        hintLineSolid: chartModel.hintLineSolid,
        crossShowList: baseModel.crossShowList,
        yStyle: yStyle,
        xStyle: xStyle,
        yValue: baseModel.yValue,
        xValue: baseModel.xValue,
        yAisTop: chartModel.yAxisTop!,
        initialPointX: chartModel.initialPointX,
        unit: chartModel.unit,
        unitStyle: chartModel.unitStyle,
        rightUnit: chartModel.rightUnit,
        rightUnitStyle: chartModel.rightUnitStyle,
        xAngleDistance: chartModel.xAngleDistance,
        xLabelDistance: chartModel.xLabelDistance,
        yRightLabelDistance: chartModel.yRightLabelDistance,
        yLabelDistance: chartModel.yLabelDistance);

    _initPath(canvas, paint, lines, lineModels,
        lineColors: baseModel.lineColors, shaderColors: baseModel.shaderColors);
    if (rightLines.isNotNullOrEmpty) {
      _initPath(canvas, paint, rightLines!, lineRightModels,
          lineColors: baseModel.rightLineColors,
          shaderColors: baseModel.rightShaderColors);
    }
    KqPadChartMethod.drawCustomDividerLine(
        canvas, baseModel.xList, baseModel.yList,
        xDivider: xDividers, yDivider: yDividers);
    KqPadChartMethod.drawAvgLine(
        canvas: canvas,
        paint: paint,
        avgYPointList: baseModel.avgYPointList,
        isAvg: chartModel.isAvg,
        xList: baseModel.xList,
        xRight: chartModel.xRight!,
        avgColors: chartModel.avgColors,
        avgValues: chartModel.avgValues ?? [],
        hintAvgLineSolid: chartModel.hintAvgLineSolid,
        initialPointX: chartModel.initialPointX,
        isAvgValue: chartModel.isAvgValue);
    _drawLine(canvas);
  }

  //计算path
  void _initPath(
    Canvas canvas,
    Paint xyPaint,
    List<List<KqPdaChartLineValue>> chartLines,
    List<LineCanvasModel> lineModel, {
    List<Color>? lineColors,
    List<List<Color>>? shaderColors,
  }) {
    for (int i = 0; i < chartLines.length; i++) {
      List<KqPdaChartLineValue> points = chartLines[i];
      Path masterPath = Path();
      if (points.isNotEmpty) {
        if (chartModel.isCurve) {
          masterPath = KqPadChartMethod.getSmoothLinePath(points);
        } else {
          masterPath.moveTo(points[0].xPoint, points[0].yPoint);
          for (int j = 1; j < points.length; j++) {
            KqPdaChartLineValue point = points[j];
            if (point.yValue.isNotNullOrEmpty) {
              masterPath.lineTo(point.xPoint, point.yPoint);
            }
          }
        }
        List paths = KqPadChartMethod.getAnimationLinePath(
            masterPath,
            points,
            baseModel.xList,
            baseModel.yList,
            chartModel.animationTime,
            chartModel.isAnimationEnd);
        Path shadowPath = paths[1];
        lineModel.add(LineCanvasModel(
          masterPath: masterPath,
          movePath: paths[2],
          points: paths.last,
          shadowPaths: shadowPath,
          lineColor: lineColors == null
              ? KqPadThemeColors.textLightBlue
              : lineColors[i],
          lineWidth: points.first.lineWidth ?? 1,
          shaderColors: shaderColors == null
              ? [
                  const Color(0x8740A9FF),
                  const Color(0x0040A9FF),
                ]
              : shaderColors[i],
        ));
      }
    }
  }

  ///绘制曲线或者折线以及文案
  void _drawLine(Canvas canvas) {
    if (lineModels.isNotNullOrEmpty) {
      for (int i = 0; i < lineModels.length; i++) {
        LineCanvasModel model = lineModels[i];
        if (lines[i].length > 1) {
          _drawLineNormal(canvas, model, i, lines);
        } else {
          _drawOnlyPoint(canvas, model, i, lines);
        }
      }
    }
    if (lineRightModels.isNotNullOrEmpty) {
      for (int i = 0; i < lineRightModels.length; i++) {
        LineCanvasModel model = lineRightModels[i];
        if (rightLines![i].length > 1) {
          _drawLineNormal(canvas, model, i, rightLines!);
        } else {
          _drawOnlyPoint(canvas, model, i, rightLines!);
        }
      }
    }
  }

  ///绘制单个点
  _drawOnlyPoint(
    Canvas canvas,
    LineCanvasModel model,
    int i,
    List<List<KqPdaChartLineValue>> chartLines,
  ) {
    if (chartModel.isAddMask) {
      Rect shadowRect = Rect.fromLTRB(baseModel.xList.first,
          baseModel.yList.first, baseModel.xList.last, baseModel.yList.last);
      var shader = LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              tileMode: TileMode.clamp,
              colors: model.shaderColors)
          .createShader(shadowRect);
      Path path = Path();
      path.moveTo(chartLines[i].first.xPoint - 2, baseModel.yList.last);
      path.lineTo(chartLines[i].first.xPoint - 2, chartLines[i].first.yPoint);
      path.lineTo(chartLines[i].first.xPoint + 2, chartLines[i].first.yPoint);
      path.lineTo(chartLines[i].first.xPoint + 2, baseModel.yList.last);
      path.lineTo(chartLines[i].first.xPoint - 2, baseModel.yList.last);
      path.close();
      canvas.drawPath(
          path,
          Paint()
            ..shader = shader
            ..isAntiAlias = true
            ..style = PaintingStyle.fill);
    }
    canvas.drawCircle(
        Offset(chartLines[i].first.xPoint, chartLines[i].first.yPoint),
        chartModel.pointRadius!,
        Paint()
          ..isAntiAlias = true
          ..strokeCap = StrokeCap.round
          ..color = model.lineColor
          ..strokeWidth = 1
          ..style = PaintingStyle.fill);
  }

  ///绘制正常曲线折线
  void _drawLineNormal(
    Canvas canvas,
    LineCanvasModel model,
    int i,
    List<List<KqPdaChartLineValue>> chartLines,
  ) {
    if (chartModel.isAddMask) {
      Rect shadowRect = Rect.fromLTRB(baseModel.xList.first,
          baseModel.yList.first, baseModel.xList.last, baseModel.yList.last);
      var shader = LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              tileMode: TileMode.clamp,
              colors: model.shaderColors)
          .createShader(shadowRect);
      canvas.drawPath(
          model.shadowPaths,
          Paint()
            ..shader = shader
            ..isAntiAlias = true
            ..style = PaintingStyle.fill);
    }
    List<KqPdaChartLineValue> info = chartLines[i];
    int firstIndex = 0;

    for (var n = 1; n < info.length; n++) {
      KqPdaChartLineValue item = info[n];
      canvas.save();
      canvas.clipRect(Rect.fromLTRB(baseModel.xList[firstIndex],
          baseModel.yList.first, baseModel.xList[n], baseModel.yList.last));
      canvas.drawPath(
          model.movePath,
          Paint()
            ..strokeWidth = item.lineWidth ?? model.lineWidth
            ..color = item.lineColor ?? model.lineColor
            ..style = PaintingStyle.stroke);
      firstIndex = n;
      canvas.restore();
    }

    for (int n = 0; n < model.points; n++) {
      if (info.length > n) {
        KqPdaChartLineValue value = info[n];
        if (chartModel.isShowPoint) {
          canvas.drawCircle(
              Offset(value.xPoint, value.yPoint),
              value.pointWidth ?? chartModel.pointRadius!,
              Paint()
                ..isAntiAlias = true
                ..strokeCap = StrokeCap.round
                ..color = value.pointColor ?? model.lineColor
                ..strokeWidth = 1
                ..style = PaintingStyle.fill);
        }
        if (chartModel.isShowValue) {
          var textValue = TextPainter(
              textAlign: TextAlign.center,
              ellipsis: '.',
              maxLines: 1,
              text: TextSpan(text: value.yValue, style: value.style),
              textDirection: TextDirection.rtl)
            ..layout();
          textValue.paint(
              canvas,
              Offset(value.xPoint - textValue.width / 2,
                  value.yPoint - textValue.height - 6));
        } else {
          if (value.isShow) {
            var textValue = TextPainter(
                textAlign: TextAlign.center,
                ellipsis: '.',
                maxLines: 1,
                text: TextSpan(text: value.yValue, style: value.style),
                textDirection: TextDirection.rtl)
              ..layout();
            textValue.paint(
                canvas,
                Offset(value.xPoint - textValue.width / 2,
                    value.yPoint - textValue.height - 6));
            if (info.first.isCountDivider) {
              var paint = Paint()
                ..strokeWidth = 1
                ..style = PaintingStyle.stroke
                ..color = info.first.dividerColor;
              Path horizontalPath = Path();
              horizontalPath.moveTo(value.xPoint, baseModel.yList.last);
              horizontalPath.lineTo(value.xPoint, value.yPoint);
              if (chartModel.hintLineSolid) {
                canvas.drawPath(horizontalPath, paint);
              } else {
                canvas.drawPath(
                  dashPath(
                    horizontalPath,
                    dashArray: CircularIntervalList<double>(<double>[4.0, 2.0]),
                  ),
                  paint,
                );
              }
              Path verticalPath = Path();
              verticalPath.moveTo(
                  baseModel.xList.first - chartModel.initialPointX,
                  value.yPoint);
              verticalPath.lineTo(value.xPoint, value.yPoint);
              if (chartModel.hintLineSolid) {
                canvas.drawPath(verticalPath, paint);
              } else {
                canvas.drawPath(
                  dashPath(
                    verticalPath,
                    dashArray: CircularIntervalList<double>(<double>[4.0, 2.0]),
                  ),
                  paint,
                );
              }
            }
          }
        }
      }
    }
  }

  @override
  bool shouldRepaint(covariant KqPdaLineChartPainter oldDelegate) {
    return true;
  }
}

//绘制图表的计算之后的结果模型集
class LineCanvasModel {
  ///遮罩层路径
  Path shadowPaths;

  ///折线或曲线动画路径
  List<Path>? pathArray;

  ///折线或曲线的主路径
  Path masterPath;

  ///折线或曲线的运动路径
  Path movePath;

  ///点的数组
  int points;

  ///遮罩层颜色
  List<Color> shaderColors;

  ///线的颜色
  Color lineColor;

  double lineWidth;

  LineCanvasModel({
    required this.masterPath,
    required this.points,
    required this.movePath,
    required this.shadowPaths,
    required this.shaderColors,
    required this.lineColor,
    this.pathArray,
    required this.lineWidth,
  });
}
